using System;
using System.Data;
using System.Configuration;
using System.Text.RegularExpressions;
using System.Collections.Generic;
using System.Text;
using System.Web.UI.WebControls;
using System.Drawing.Imaging;

/// <summary>
/// Basic Utility Static Methods
/// </summary> 

namespace CodeWork.Library
{
    public class Utility
    {
        /// <summary>
        /// Private constructor to restrict from instantiating this class
        /// </summary>
        private Utility() { }

        /// <summary>
        /// This method is used with List page aspx to generate custom paging
        /// It returns the list of integers that is shown as page numbers.
        /// It divides the total items with the items to be shown per page, 
        /// to count the amount of pages.
        /// </summary>
        /// <param name="totalItem">Total items in the database that can be shown</param>
        /// <param name="pageSize">Items to be shown per page</param>
        /// <returns>A list of integers that acts as page numbers ( Zero indexed )</returns>
        public static List<int> PreparePagingData(int totalItem, int pageSize)
        {
            List<int> pages = new List<int>();

            // If page size is not a positive number, return the empty list
            if (pageSize <= 0)
                return pages;

            // Divide the total items with page size to count how many pages are there
            for (int i = 0; i <= (totalItem - 1) / pageSize; i++)
                pages.Add(i);

            return pages;
        }

        /// <summary>
        /// Used for sorting data grids. It converts the Sort direction to a proper
        /// string based sort direction that is used with the custom sorting of the
        /// grid.
        /// </summary>
        /// <param name="sortDirection">Current sort direction</param>
        /// <returns>string representation of the direction</returns>
        public static string ConvertSortDirectionToSql(SortDirection sortDirection)
        {
            string newSortDirection = String.Empty;

            switch (sortDirection)
            {
                case SortDirection.Ascending:
                    newSortDirection = "ASC";
                    break;
                case SortDirection.Descending:
                    newSortDirection = "DESC";
                    break;
            }
            return newSortDirection;
        }

        /// <summary>
        /// Creates a comma seperated value string from distinct
        /// integer list
        /// </summary>
        /// <param name="items">The list of integers that should be converted</param>
        /// <returns>The comma seperated value string</returns>
        public static string MakeCSV(List<int> items)
        {
            StringBuilder result = new StringBuilder(string.Empty);

            for (int i = 0; i < items.Count; i++)
            {
                // If not first item, add a comma than add the value, otherwise
                // just add the value
                if (i != 0)
                    result = result.Append(",").Append(items[i]);
                else
                    result = result.Append(items[i]);
            }

            return result.ToString();
        }

        /// <summary>
        /// Creates a comma seperated value string from distinct
        /// string list
        /// </summary>
        /// <param name="items"></param>
        /// <returns></returns>
        public static string MakeCSV(List<string> items)
        {
            StringBuilder result = new StringBuilder(string.Empty);

            for (int i = 0; i < items.Count; i++)
            {
                // If not first item, add a comma than add the value, otherwise
                // just add the value
                if (i != 0)
                    result = result.Append(",").Append(items[i]);
                else
                    result = result.Append(items[i]);
            }

            return result.ToString();
        }

        /// <summary>
        /// Converts a comma seperated list of string to a list of individual string values
        /// </summary>
        /// <param name="csv">The string representation of the comma seperated values</param>
        /// <returns>A list of strings</returns>
        public static List<string> SplitCSV(string csv)
        {
            List<string> result = new List<string>();
            string[] parts = csv.Split(',');

            foreach (string part in parts)
            {
                result.Add(part);
            }

            return result;
        }

        /// <summary>
        /// Converts a comma seperated list of string to a list of integer values
        /// </summary>
        /// <param name="csv">The string representation of the comma seperated values</param>
        /// <returns>A list of integers</returns>
        public static List<int> SplitCSVInt(string csv)
        {
            List<int> result = new List<int>();
            string[] parts = csv.Split(',');

            foreach (string part in parts)
            {
                int parsedValue = 0;

                // If can parse the text, than added in the list
                if(int.TryParse(part.Trim(), out parsedValue))
                    result.Add(parsedValue);
            }

            return result;
        }

        /// <summary>
        /// Shortens a long text by a specified length. Userful
        /// to show short descriptions.
        /// </summary>
        /// <param name="text">The long text</param>
        /// <param name="maxLength">The maximum length for the returing text</param>
        /// <returns>The shortened text</returns>
        public static string ShortenText(string text, int maxLength)
        {
            StringBuilder newText = new StringBuilder();

            // If length is greather than the max length specified
            // then format it, otherwise return the actual text
            if (text.Length > maxLength)
            {
                // Cut the text and add three dots as continuation sign
                newText.Append(text.Substring(0, maxLength - 3));
                newText.Append(" ...");
                return newText.ToString();
            }
            else
                return text;
        }

        /// <summary>
        /// Converts an underscore seperated text to Hungarian notation format
        /// For example: from "email_address" to "EmailAddress"
        /// This method is useful for creating generators and other
        /// naming conversion
        /// </summary>
        /// <param name="text">Text to be converted</param>
        /// <returns>Converted hungarian notationed text</returns>
        public static string ToHungarianNotation(string text)
        {
            StringBuilder result = new StringBuilder(text);

            for (int i = 0; i < result.Length; i++)
            {                
                if (result[i] == '_')
                {
                    result[i + 1] = Char.ToUpper(result[i + 1]);
                    result.Remove(i, 1);
                }
                if (i == 0)
                    result[i] = Char.ToUpper(result[i]);
            }
            return result.ToString();
        }

        /// <summary>
        /// Converts an underscore seperated text in CamelCased form
        /// For example: from "email_address" to "emailAddress"
        /// This method is useful for creating generators and other
        /// naming conversion
        /// </summary>
        /// <param name="text">Text to be convert</param>
        /// <returns>Converted camel cased text</returns>
        public static string ToCamelCase(string text)
        {
            StringBuilder result = new StringBuilder(text);

            // Check every character
            for (int i = 0; i < result.Length; i++)
            {
                // If the character is underscore
                if (result[i] == '_')
                {
                    // If it is not the last character, upcase the next character
                    if (i < result.Length - 1)
                    {
                        result[i + 1] = Char.ToUpper(result[i + 1]);
                    }
                    // Remove the underscore
                    result.Remove(i, 1);
                }
                // If it is the first character, just down case it
                // As underscore is already removed by the above code
                // so case like this _happyExample is already HappyExample
                // and we just need to downcase the first character
                if (i == 0)
                    result[i] = Char.ToLower(result[i]);
            }
            return result.ToString();
        }

        /// <summary>
        /// Converts an underscore seperated text in Capitalized form.
        /// For example: from "eamil_address" to "EMAILADDRESS" or "EMAIL_ADDRESS
        /// based on the removeUnderscore value. This method is useful for
        /// Creating generators or constant Names.
        /// </summary>
        /// <param name="text">Text to be convert</param>
        /// <param name="removeUnderscore">Whether to remove underscore or not</param>
        /// <returns>The converted capitalized text</returns>
        public static string ToCapitalCase(string text, bool removeUnderscore)
        {
            if (removeUnderscore)
                return text.Replace("_", "").ToUpper();
            else
                return text.ToUpper();
        }

        /// <summary>
        /// Check whether the datetime has been set, returns null otherwise.
        /// very useful to use in entity to filter null datetime to convert
        /// into SqlDateTime which has higher minvalue and throws exception
        /// if the DateTime.MinValue is passed
        /// </summary>
        /// <param name="dateTime">The original datetime</param>
        /// <returns>If the datetime is DateTime.MinValue than null otherwise the original DateTime</returns>
        public static DateTime? FilterNullDateTime(DateTime dateTime)
        {
            if (dateTime == DateTime.MinValue)
                return null;
            else
                return dateTime;
        }

        /// <summary>
        /// Randomly picks 'k' numbers form a sequence that was not
        /// picked last time. This method is not properly tested
        /// yet. The max range that this method can randomize is 
        /// 10e6 - 1
        /// </summary>
        /// <param name="totalItem">Range of the sequence, starting from zero</param>
        /// <param name="itemNeeded">How many items to return</param>
        /// <param name="lastUsed">The items that are used last time, used for skipping purpose</param>
        /// <returns>If the range is more than 10e6 -1, an empty list is returned,otherwise a list of k numbers</returns>
        public static List<int> RandomizeList(int totalItem, int itemNeeded, List<int> lastUsed)
        {
            // The maximum range that this function can accept
            const int MAX_RANGE = 1000000;

            // If range is more than the MAX_RANGE, return empty list
            if (totalItem >= MAX_RANGE)
                return new List<int>();
                      
            bool[] items = new bool[MAX_RANGE];

            // Initialize the flags with true
            for (int k = 0; k < lastUsed.Count; k++)
                items[k] = true;
            

            Random random = new Random();
            List<int> result = new List<int>();

            // While we do not have enough items
            int itemFound = 0;
            while (itemFound < itemNeeded)
            {
                // Pick a number in the range
                int temp = random.Next(0, totalItem);

                // If it is not already picked
                if (!items[temp])
                {
                    // Mark it as picked and add it in the list of
                    // found items
                    items[temp] = true;
                    result.Add(temp);
                    itemFound++;
                }
            }

            return result;
        }

        /// <summary>
        /// Converts 24 hour time to 12 hour time string that is parsable
        /// </summary>
        /// <param name="hour">Hour component of 24 hour time</param>
        /// <param name="minute">Minute component of 24 hour time</param>
        /// <param name="second">Second component of 24 hour time</param>
        /// <returns>12 hour time as string, parsable by DateTime.Parse() or Convert.ToDateTime()</returns>
        public static string ConvertTo12HourTime(int hour, int minute, int second)
        {
            StringBuilder result = new StringBuilder();

            string amOrPm = string.Empty;

            // Considering the following extreme cases.
            // Number before decimal means hour, and number
            // after decimal means minute
            // 00.01, 00.00, 23.59, 11.59, 12.00, 12.01

            // Zero hour means hour 24
            if (hour == 0)
                hour = 24;

            // If hour greater than 12 than we are in PM zone
            // else if hour is equal to 12 hour but second
            // or minute is greater than zero than also we are
            // in PM zone otherwise we are in AM zone
            if (hour > 12)
            {
                hour -= 12;
                amOrPm = "PM";
            }
            else if (hour == 12)
            {
                if (minute + second > 0)
                    amOrPm = "PM";
            }
            else
                amOrPm = "AM";

            // Appending and formatting the text to return 12 hour time in the
            // Following format: 03:36:14 PM
            result.Append(hour.ToString("00")).Append(":").Append(minute.ToString("00")).Append(":").Append(second.ToString("00")).Append(" ").Append(amOrPm);

            return result.ToString();
        }

        /// <summary>
        /// Converts 24 hour time to 12 hour time string that is parsable
        /// A sample 24 hour time that this method can parse is: 15:68:22
        /// </summary>
        /// <param name="timeIn24Hour">24 hour time string</param>
        /// <param name="seperator">character that seperates the hour, minute, second components</param>
        /// <returns>12 hour time as string, parsable by DateTime.Parse() or Convert.ToDateTime()</returns>
        public static string ConvertTo12HourTime(string timeIn24Hour, char[] seperator)
        {
            StringBuilder result = new StringBuilder();

            // Seperating the the time string
            string[] timeParts = timeIn24Hour.Split(seperator);

            // If more than two parts are found than we can convert it
            // otherwise there is hour or minute or second is missing
            // and as a result null is returned
            if (timeParts.Length > 2)
            {
                // Initializing the variables before calculation
                string amOrPm = string.Empty;
                string actualHour = timeParts[0].Trim();
                string actualMinute = timeParts[1].Trim();
                string actualSecond = timeParts[2].Trim();

                int hour = 0;
                int minute = 0;
                int second = 0;

                // Trying to parse the time components (Hour, Minute, Second)
                // if failed zero is considered
                if (!string.IsNullOrEmpty(actualHour))
                {
                    if (!int.TryParse(actualHour, out hour))
                        hour = 0;
                }
                if (!string.IsNullOrEmpty(actualMinute))
                {
                    if (!int.TryParse(actualMinute, out minute))
                        minute = 0;
                }
                if (!string.IsNullOrEmpty(actualSecond))
                {
                    if (!int.TryParse(actualSecond, out second))
                        second = 0;
                }

                // Calling the other helper method that actually 
                // process the Hour, minute, second and returns a 
                // string representaion of the 12 hour time
                return ConvertTo12HourTime(hour, minute, second);
            }
            else
                return null;
        }

        /// <summary>
        /// Creates a randomly generated Unique Code. The code can
        /// be used in confirmation email in the link, as parameter
        /// and can be matched to verify a returning user. Other
        /// uses are also possilbe.
        /// </summary>
        /// <returns>A string containing the code</returns>
        public static string CreateConfirmationCode()
        {
            // Using the GUID as confirmation code without the dash
            return Guid.NewGuid().ToString().Replace("-","");
        }

        /// <summary>
        /// Returns a ImageFormat Enum from the string name
        /// </summary>
        /// <param name="formatName">Format name string</param>
        /// <param name="isExtension">Indicates whether the name is an extension</param>
        /// <returns>Appropiate ImageFormat</returns>
        public static ImageFormat GetImageFormat(string formatName, bool isExtension)
        {
            formatName = formatName.ToUpper();
            if (isExtension)
            {
                if (formatName == ".JPG" || formatName == ".JPEG")
                    return ImageFormat.Jpeg;
                else if (formatName == ".PNG")
                    return ImageFormat.Png;
                else if (formatName == ".GIF")
                    return ImageFormat.Gif;
                else if (formatName == ".BMP")
                    return ImageFormat.Bmp;
                else if (formatName == ".TIFF")
                    return ImageFormat.Tiff;
                else
                    return ImageFormat.Jpeg;
            }
            else
            {
                if (formatName == "JPG" || formatName == "JPEG")
                    return ImageFormat.Jpeg;
                else if (formatName == "PNG")
                    return ImageFormat.Png;
                else if (formatName == "GIF")
                    return ImageFormat.Gif;
                else if (formatName == "BMP")
                    return ImageFormat.Bmp;
                else if (formatName == "TIFF")
                    return ImageFormat.Tiff;
                else
                    return ImageFormat.Jpeg;
            }
        }
    }
}
